﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace HQS.TokenInterpolation.Test
{
    [TestClass]
    public class InterpolationUnitTests
    {
        [TestMethod]
        public void TestingTemplates()
        {
            const string template = @"
Dear {Name}

Here are the items:

{foreach this:{Orders} template:Order}
@template (name:Order)
{
{padright this:{Order.Current.Name} length:20}{padright this:{Order.Current.Sku} length:20}{padright this:{Order.Current.Price:C} length:20}{padright this:{compute format:C2 expression:{Order.Current.Price} * 1.06} length:20}  
}

";

            var context =
                new DataSourcesVisitor(
                    new
                    {
                        Name = "John Smith",
                        Orders = new List<object>
                        {
                            new {Name = "Phone Charger", Sku = "11AA", Price = 100},
                            new {Name = "Phone Cover", Sku = "33BCDEF", Price = 24}
                        }
                    });

            Result result = Parser.Parse(template, context);
            string str = result.Output;
        }

        [TestMethod]
        public void Test()
        {
            const string template = @"
            {Env.Today format:F}
            {Company.Name}
            {Company.Address.Street}
            {Company.Address.City}, {Company.Address.State} {Company.Address.Zip}
            ";

            var anonymousObj = new
            {
                Name = "John Smith",
            };

            var company = new
            {
                Name = "Bankrate, Inc",
                Address =
                    new {Street = "11760 US HWY 1 SUIT 200", City = "Palm Beach Garden", State = "FL", Zip = "33425"}
            };

            string templateResolved = template.Replace("{Env.Today format:F}", DateTime.Today.ToString("F"))
                .Replace("{Company.Name}", company.Name)
                .Replace("{Company.Address.Street}", company.Address.Street)
                .Replace("{Company.Address.City}", company.Address.City)
                .Replace("{Company.Address.State}", company.Address.State)
                .Replace("{Company.Address.Zip}", company.Address.Zip);


            var context = new DataSourcesVisitor(anonymousObj);
            context["Company"] = company;
            context["Env"] = new EnvironmentParser();
            Result result = Parser.Parse(template, context);
            Assert.AreEqual(result.Output, templateResolved);
        }

        [TestMethod]
        public void TestNotSupportedToken()
        {
            const string template = "Hello {NotExistName}";
            var context = new DataSourcesVisitor(new {Name = "Name", Age = 2});
            Result result = Parser.Parse(template, context);
            Assert.AreEqual("Hello ", result.Output);
            Assert.IsFalse(result.TokenAndValues.First().Value.Resolved);
        }

        [TestMethod]
        public void TestNotSupportedRoot()
        {
            const string template = "Hello {Root.Child.NotExist}";
            var context = new DataSourcesVisitor();
            context.AddOrUpdate("Root", new {Child = new {Test = "F"}});
            string result = Parser.Parse(template, context).Output;
        }

        [TestMethod]
        public void TestSuppressedExceptions()
        {
            const string template = "Hello {Root.Child.NotExist}";
            var context = new DataSourcesVisitor();
            context.AddOrUpdate("Root", new {Child = new {Test = "F"}});
            string result = Parser.Parse(template, context).Output;
        }

        [TestMethod]
        public void TestDictionary()
        {
            const string template = "Hello {NotExist}";
            var data = new Dictionary<string, object> {{"NotExist", "YES"}};
            var context = new DataSourcesVisitor(data);
            string result = Parser.Parse(template, context).Output;

            Assert.AreEqual(result, "Hello " + data.First().Value);
        }

        [TestMethod]
        public void TestFieldAcess()
        {
            const string template =
                "On his {Root.Term} term, the president {Root.Name} has named vice president {Root.VicePresident.Name}.";
            var president = new President();
            president.VicePresident = new President {Name = "Joe Biden"};
            president.Name = "Barak Obama";
            var context = new DataSourcesVisitor();
            context.AddOrUpdate("Root", president);
            Result result = Parser.Parse(template, context);
            Assert.AreEqual(result.Output,
                "On his 2 term, the president Barak Obama has named vice president Joe Biden.");
        }

        [TestMethod]
        public void TestISupportParser()
        {
            const string template = "Hello {Today format:F}";
            var data = new EnvironmentParser();
            var context = new DataSourcesVisitor(data);
            string result = Parser.Parse(template, context).Output;

            Assert.AreEqual(result, "Hello " + DateTime.Today.ToString("F"));
        }

        [TestMethod]
        public void TestNullMiddleProperty()
        {
            const string template = "Hello {Test.Property1.Property2.Length}";
            var context = new DataSourcesVisitor();
            context.AddOrUpdate("Test", new {Property1 = new {Property2 = (string) null}});
            Result result = Parser.Parse(template, context);
            Assert.IsTrue(result.ErrorMessages.Any(f => f.Contains("Test.Property1.Property2")));
        }

        private class EnvironmentParser : IResolveToken
        {
            private readonly Dictionary<string, Func<object>> _env = new Dictionary<string, Func<object>>();

            public EnvironmentParser()
            {
                _env.Add("Now", () => DateTime.Now);
                _env.Add("Today", () => DateTime.Today);
            }

            public TokenRuntimeValue Resolve(string tokenHandle)
            {
                var value = new TokenRuntimeValue();

                Func<object> func;
                if (_env.TryGetValue(tokenHandle, out func))
                {
                    try
                    {
                        value.Resolved = true;
                        value.RawValue = func.Invoke();
                    }
                    catch
                    {
                        value.ErrorMessage = "Not found";
                        value.Resolved = false;
                    }
                }

                return value;
            }

            public object Resolve(string tokenHandle, object defaultValue)
            {
                Func<object> result;
                if (_env.TryGetValue(tokenHandle, out result))
                {
                    return result.Invoke();
                }

                return defaultValue;
            }
        }

        private class Order
        {
            public string Name;
            public int OrderNo;
        }

        private class President
        {
            public int Term = 2;
            public string Name { get; set; }

            public President VicePresident { get; set; }
        }
    }
}